{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "这次尽量用故事模式来讲知识\n",
    "\n",
    "## 1.定义一个类\n",
    "类的组成：类名、属性（没有字段）、方法\n",
    "### 1.1创建一个类"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 类名首字母大写\n",
    "class Student(object):\n",
    "    \"\"\"创建一个学生类\"\"\"\n",
    "    # 没有属性定义，直接使用即可\n",
    "    # 定义一个方法，方法里面必须有self（相当于C#的this）\n",
    "    def show(self):\n",
    "        print(\"name:%s age:%d\"%(self.name,self.age))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "name:张三 age:22\n"
     ]
    }
   ],
   "source": [
    "# 实例化一个张三\n",
    "zhangsan=Student()\n",
    "# 给name，age属性赋值\n",
    "zhangsan.name=\"张三\"\n",
    "zhangsan.age=22\n",
    "# 调用show方法\n",
    "zhangsan.show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "<class '__main__.Student'>\n",
      "<__main__.Student object at 0x7fe961195b70>\n"
     ]
    }
   ],
   "source": [
    "# 打印一下类和类的实例\n",
    "print(Student)\n",
    "print(zhangsan) #张三实例的内存地址：0x7fb6e8502d30"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "和静态语言不同，Python允许**对实例变量绑定任何数据** ==> 对于两个实例变量，**虽然它们都是同一个类的不同实例，但拥有的变量名称可能都不同**\n",
    "\n",
    "说的比较抽象，举个例子就明了了："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "ename": "TypeError",
     "evalue": "object() takes no parameters",
     "output_type": "error",
     "traceback": [
      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[0;31mTypeError\u001b[0m                                 Traceback (most recent call last)",
      "\u001b[0;32m<ipython-input-4-500940527165>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m()\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mxiaoming\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mStudent\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"小明\"\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;36m22\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m      2\u001b[0m \u001b[0mxiaoming\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mmmd\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;34m\"mmd\"\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m      3\u001b[0m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mxiaoming\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mmmd\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m      4\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m      5\u001b[0m \u001b[0;31m# 小明和小潘都是Student类，但是小明有的mmd属性，小潘却没有\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
      "\u001b[0;31mTypeError\u001b[0m: object() takes no parameters"
     ]
    }
   ],
   "source": [
    "xiaoming=Student(\"小明\",22)\n",
    "xiaoming.mmd=\"mmd\"\n",
    "print(xiaoming.mmd)\n",
    "\n",
    "# 小明和小潘都是Student类，但是小明有的mmd属性，小潘却没有\n",
    "xiaopan=Student(\"小潘\",22)\n",
    "print(xiaopan.mmd)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 1.2使用\\_\\_init\\_\\_初始化赋值\n",
    "\n",
    "创建对象后，python解释器默认调用**\\__init\\__**方法，对必要字段进行初始化赋值\n",
    "\n",
    "需要注意的是：**\\_\\_init\\_\\_**并不是C#中的构造函数，**\\_\\_new\\_\\_** （后面会说） + **\\_\\_init\\_\\_** 等价于构造函数\n",
    "\n",
    "第一个参数和类的其他方法一样，都是self（相当于C#里面的this，表示创建的实例本身）调用的时候直接忽略它"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Student(object):\n",
    "    # 初始化赋值\n",
    "    def __init__(self,name,age):\n",
    "        self.name=name\n",
    "        self.age=age\n",
    "    \n",
    "    def show(self):\n",
    "        print(\"name:%s age:%d\"%(self.name,self.age))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "ename": "TypeError",
     "evalue": "__init__() missing 2 required positional arguments: 'name' and 'age'",
     "output_type": "error",
     "traceback": [
      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[0;31mTypeError\u001b[0m                                 Traceback (most recent call last)",
      "\u001b[0;32m<ipython-input-6-1ba88e24910b>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m()\u001b[0m\n\u001b[1;32m      1\u001b[0m \u001b[0;31m# 有了__init__方法，在创建实例的时候，就不能传入空的参数了\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 2\u001b[0;31m \u001b[0mlisi\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mStudent\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
      "\u001b[0;31mTypeError\u001b[0m: __init__() missing 2 required positional arguments: 'name' and 'age'"
     ]
    }
   ],
   "source": [
    "# 有了__init__方法，在创建实例的时候，就不能传入空的参数了\n",
    "lisi=Student()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "name:小王 age:22\n"
     ]
    }
   ],
   "source": [
    "# 创建一个正确的实例\n",
    "xiaowang=Student(\"小王\",22)\n",
    "xiaowang.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 1.3使用魔法方法\\__str\\__\n",
    "\n",
    "在print(类名)的时候自定义输出\n",
    "\n",
    "这个有点像C#类里面重写ToString，eg:\n",
    "\n",
    "```csharp\n",
    "public override string ToString()\n",
    "{\n",
    "    return \"Name:\" + this.Name + \" Age:\" + this.Age;\n",
    "}\n",
    "```"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Python的__str__()方法\n",
    "class Student(object):\n",
    "    def __init__(self, name, age):\n",
    "        self.name = name\n",
    "        self.age = age\n",
    "\n",
    "    # self别忘记写了，return也别忘了\n",
    "    def __str__(self):\n",
    "        return \"姓名：%s，年龄：%s\" % (self.name, self.age)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "姓名：李四，年龄：22\n"
     ]
    }
   ],
   "source": [
    "lisi = Student(\"李四\", 22)\n",
    "print(lisi) #现在打印就是你DIV的输出了"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 1.4 私有属性、方法\n",
    "\n",
    "C#、Java里面都是有**访问修饰符**的，Python呢？\n",
    "\n",
    "Python规定，如果**以双下划线\\__开头**的属性或者方法**就是私有的**\n",
    "\n",
    "变量名类似**__xxx__**的，也就是以双下划线开头，并且以双下划线结尾的，**是特殊变量**。特殊变量是可以直接访问的，**不是private变量**\n",
    "\n",
    "在说私有属性前，我们来个案例说说属性不私有的**弊端**，eg:\n",
    "\n",
    "小明同学学了点C#，然后学习了上面的知识，心想 ～ Python这么搞安全性呢？不行，我得构造构造，于是有了下面的代码："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Student(object):\n",
    "    def __init__(self, name, age):\n",
    "        self.name = name\n",
    "        self.age = age\n",
    "\n",
    "    def get_name(self):\n",
    "        return self.name\n",
    "\n",
    "    def set_name(self, name):\n",
    "        self.name = name\n",
    "\n",
    "    def get_age(self):\n",
    "        return self.age\n",
    "\n",
    "    def set_age(self, age):\n",
    "        if age > 0:\n",
    "            self.age = age\n",
    "        else:\n",
    "            print(\"age must > 0\")\n",
    "\n",
    "    def show(self):\n",
    "        print(\"name:%s,age:%d\" % (self.name, self.age))\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "小明心想，想要修改age属性，你通过set_age我就可以判断了哇，还是本宝宝聪明\n",
    "\n",
    "这时候小潘过来了，淡淡的一笑，看我怎么破了你 ～ 看代码："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "name:张三,age:-20\n",
      "name:张三,age:-1\n"
     ]
    }
   ],
   "source": [
    "zhangsan = Student(\"张三\", -20)\n",
    "zhangsan.show()  # name:张三,age:-20\n",
    "zhangsan.age = -1  # set_age方法形同虚设，我完全可以直接访问字段了\n",
    "zhangsan.show()  # name:张三,age:-1"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "小潘傲气的说道～大叔，给你脸呢。我就是不去访问你设定的方法怎么滴呢？\n",
    "\n",
    "小明急的啊，赶紧去找伟哥求经。不一会，傲气的贴出自己的New Code，心想着我私有属性都用上了还怕个毛毛："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Student(object):\n",
    "    def __init__(self, name, age):\n",
    "        self.__name = name\n",
    "        # 一般需要用到的属性都直接放在__init__里面了\n",
    "        # self.__age = age\n",
    "        self.set_age(age)\n",
    "\n",
    "    def get_name(self):\n",
    "        return self.__name\n",
    "\n",
    "    def set_name(self, name):\n",
    "        self.__name = name\n",
    "\n",
    "    def get_age(self):\n",
    "        return self.__age\n",
    "\n",
    "    def set_age(self, age):\n",
    "        if age > 0:\n",
    "            self.__age = age\n",
    "        else:\n",
    "            print(\"age must > 0\")\n",
    "\n",
    "    def show(self):\n",
    "        print(\"name:%s,age:%s\" % (self.__name, self.__age))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "小潘冷笑道～呵呵，然后使用了上次的绝招："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "age must > 0\n"
     ]
    },
    {
     "ename": "AttributeError",
     "evalue": "'Student' object has no attribute '_Student__age'",
     "output_type": "error",
     "traceback": [
      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[0;31mAttributeError\u001b[0m                            Traceback (most recent call last)",
      "\u001b[0;32m<ipython-input-13-82c41ff46846>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m()\u001b[0m\n\u001b[1;32m      1\u001b[0m \u001b[0mzhangsan\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mStudent\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"张三\"\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m-\u001b[0m\u001b[0;36m20\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m      2\u001b[0m \u001b[0mzhangsan\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m__age\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m-\u001b[0m\u001b[0;36m1\u001b[0m  \u001b[0;31m# 同样的代码，只是属性前面加了下划线\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 3\u001b[0;31m \u001b[0mzhangsan\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mshow\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
      "\u001b[0;32m<ipython-input-12-1dec32486a19>\u001b[0m in \u001b[0;36mshow\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m     22\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m     23\u001b[0m     \u001b[0;32mdef\u001b[0m \u001b[0mshow\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 24\u001b[0;31m         \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"name:%s,age:%s\"\u001b[0m \u001b[0;34m%\u001b[0m \u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m__name\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m__age\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
      "\u001b[0;31mAttributeError\u001b[0m: 'Student' object has no attribute '_Student__age'"
     ]
    }
   ],
   "source": [
    "zhangsan = Student(\"张三\", -20)\n",
    "zhangsan.__age = -1  # 同样的代码，只是属性前面加了下划线\n",
    "zhangsan.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "这次小潘同志傻眼了，完全**不能访问**了啊？不行，怎么能被小明大叔笑话呢？\n",
    "\n",
    "于是上网翻资料，国内不行就国外，外文不好就翻译，终于找到一个新破解方式：\n",
    "\n",
    "双下划线开头的实例变量不能直接访问,是因为Python解释器对外把\\__age变量改成了\\_Student\\__age，所以，仍然可以通过**\\_Student\\__age**来访问："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "name:张三,age:-1\n"
     ]
    }
   ],
   "source": [
    "# 搞事情\n",
    "zhangsan._Student__age = -1\n",
    "zhangsan.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "建议你不要这么干，不同版本的Python解释器可能会把\\__age改成不同的变量名\n",
    "\n",
    "有些时候，你会看到**以一个下划线开头的实例变量名**，比如_age这样的实例变量,外部是可以访问的。\n",
    "\n",
    "但是，**请把它视为私有变量**，不要随意访问（**Python很多东西全凭自觉**~捂脸@_@）\n",
    "\n",
    "小潘终于长叹一口气，然后还不忘取笑小明同学～**你这属性搞的，真麻烦，总是通过方法调用，太累了** <_> 鄙视!\n",
    "\n",
    "这可把小明急的啊，学习的积极性都没有了，吃了碗牛肉面就去伟哥那边好好取经了～"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "ename": "AttributeError",
     "evalue": "'Student' object has no attribute '__go_home'",
     "output_type": "error",
     "traceback": [
      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[0;31mAttributeError\u001b[0m                            Traceback (most recent call last)",
      "\u001b[0;32m<ipython-input-15-45c76191b808>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m()\u001b[0m\n\u001b[1;32m      7\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m      8\u001b[0m \u001b[0mzhangsan\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mStudent\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 9\u001b[0;31m \u001b[0mzhangsan\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m__go_home\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;31m# 访问不到\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
      "\u001b[0;31mAttributeError\u001b[0m: 'Student' object has no attribute '__go_home'"
     ]
    }
   ],
   "source": [
    "# 私有方法一笔带过\n",
    "class Student(object):\n",
    "    \"\"\"私有方法\"\"\"\n",
    "    def __go_home(self):\n",
    "        pass\n",
    "\n",
    "\n",
    "zhangsan = Student()\n",
    "zhangsan.__go_home() # 访问不到"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 1.5 装饰器，让方法像属性那样便利\n",
    "\n",
    "Python内置的`@property`装饰器就是负责把一个方法变成属性调用的，来个例子"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Student(object):\n",
    "    def __init__(self, name, age):\n",
    "        # 一般需要用到的属性都直接放在__init__里面了\n",
    "        self.name = name\n",
    "        self.age = age\n",
    "\n",
    "    @property\n",
    "    def name(self):\n",
    "        return self.__name\n",
    "\n",
    "    @name.setter\n",
    "    def name(self, name):\n",
    "        self.__name = name\n",
    "\n",
    "    @property\n",
    "    def age(self):\n",
    "        return self.__age\n",
    "\n",
    "    @age.setter\n",
    "    def age(self, age):\n",
    "        if age > 0:\n",
    "            self.__age = age\n",
    "        else:\n",
    "            print(\"age must > 0\")\n",
    "\n",
    "    def show(self):\n",
    "        print(\"name:%s,age:%s\" % (self.name, self.age))\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "age must > 0\n",
      "name:小潘,age:22\n"
     ]
    }
   ],
   "source": [
    "xiaoming = Student(\"小明\", 22)\n",
    "xiaoming.name = \"小潘\"\n",
    "xiaoming.age = -2\n",
    "xiaoming.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "把一个getter**方法变成属性**，只需要加上`@property`就可以了\n",
    "\n",
    "`@方法名.setter`，负责把一个setter方法变成**属性赋值**\n",
    "\n",
    "当然了，如果只想读 ==> 就只打上`@property`标签\n",
    "\n",
    "小明同学高兴坏了，赶紧大吃了一顿～\n",
    "\n",
    "---\n",
    "\n",
    "### 1.6 \\__del\\__ and \\__new\\__\n",
    "\n",
    "创建对象后，python解释器默认调用**\\__init\\__()** 方法\n",
    "\n",
    "当删除一个对象时，python解释器也会默认调用**\\__del\\__()** 方法（有点析构函数的味道）\n",
    "\n",
    "当有1个变量保存了对象的引用时，此对象的引用**计数就会加1**\n",
    "\n",
    "当使用del删除变量指向的对象时，**如果对象的引用计数不为1**，那么**每次删除计数减1**，当**计数为1的时候再调del就真把对象删了**\n",
    "\n",
    "这个可以结合我之前说过的链接来理解：<a href=\"http://www.cnblogs.com/dunitian/p/8046389.html\" target=\"_blank\">于链接文件的探讨</a>\n",
    "\n",
    "看着老师夸夸其谈，小明愣了楞，摸摸肚子想到，真BB，我先搞个例子练练："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 定义一个临时类\n",
    "class Temp(object):\n",
    "    def __del__(self):\n",
    "        print(\"你被干掉了\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "验证方面用编辑器比较合适，交互模式下可能不是真正的结果\n",
    "```py\n",
    "# 对象被s1和s2引用\n",
    "s1 = Temp()\n",
    "s2 = s1\n",
    "\n",
    "del s1  # 只删除s1，新创建的对象并没有被删除\n",
    "print(\"-\" * 10)\n",
    "```\n",
    "输出：（最后的被干掉是程序退出了）\n",
    "```\n",
    "# ----------\n",
    "# 你被干掉了\n",
    "```\n",
    "如果用链接来解释就是这样的：\n",
    "![ln来解释](https://images2018.cnblogs.com/blog/1127869/201806/1127869-20180620101248617-1569990298.png)\n",
    "\n",
    "这次两个都删掉：\n",
    "```py\n",
    "t1 = Temp()\n",
    "t2 = t1\n",
    "\n",
    "del t1\n",
    "del t2\n",
    "print(\"-\" * 10)\n",
    "```\n",
    "输出：\n",
    "```\n",
    "# 你被干掉了\n",
    "# ----------\n",
    "```\n",
    "都删了，自然就真删掉了\n",
    "\n",
    "---\n",
    "\n",
    "这样搞比较麻烦，我们引入一下**获取引用个数:getrefcount**（object也会占1个引用计数）来个案例：\n",
    "```py\n",
    "# 程序退出的时候，在他运行期间所有占用资源归还操作系统\n",
    "# 引用计数\n",
    "import sys\n",
    "t1 = Temp()\n",
    "print(sys.getrefcount(t1))  #（结果比实际引用大1）【object也会占1个引用计数】\n",
    "\n",
    "t2 = t1\n",
    "print(sys.getrefcount(t1))\n",
    "print(sys.getrefcount(t2))\n",
    "\n",
    "del t1\n",
    "print(sys.getrefcount(t2))\n",
    "# sys.getrefcount(t1)#被删掉自然没有了\n",
    "\n",
    "del t2\n",
    "print(\"-\" * 10)\n",
    "```\n",
    "运行结果：\n",
    "```\n",
    "2\n",
    "3\n",
    "3\n",
    "2\n",
    "你被干掉了\n",
    "----------\n",
    "```\n",
    "\n",
    "我再贴一种情况，你可以思考下为啥：\n",
    "```py\n",
    "t1 = Temp()\n",
    "t2 = Temp()\n",
    "\n",
    "del t1\n",
    "del t2\n",
    "print(\"-\" * 10)\n",
    "```\n",
    "输出：\n",
    "```\n",
    "# 你被干掉了\n",
    "# 你被干掉了\n",
    "# ----------\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "---\n",
    "\n",
    "小潘扭过头瞅了一眼说道：“大叔，你\\__new\\__忘记写案例了”\n",
    "\n",
    "小明一愣神，立马反应过来说：“我这叫谋而后动～”\n",
    "\n",
    "当你实例化一个对象的时候，就会执行new 方法里面的方法。new方法在类定义中不是必须写的，如果没定义，默认会调用object.new去创建一个对象\n",
    "\n",
    "\\__new\\__方法中至少要有一个参数**cls**，代表要实例化的类，此参数在实例化时由Python解释器自动提供\n",
    "\n",
    "\\__new\\__方法中必须要**有返回值**(返回实例化出来的实例)\n",
    "\n",
    "小明翻阅了官方文档，淡定的打下了如下标准格式的代码："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "创建对象完毕\n",
      "初始化完毕\n",
      "Dog的名字叫：Happy\n"
     ]
    }
   ],
   "source": [
    "class Dog(object):\n",
    "    def __init__(self, name):\n",
    "        self.name = name\n",
    "        print(\"初始化完毕\")\n",
    "\n",
    "    def __str__(self):\n",
    "        return \"Dog的名字叫：%s\" % self.name\n",
    "\n",
    "    def __new__(cls, name):\n",
    "        # 注意参数，是cls，然后其他参数和init保持一致即可\n",
    "        print(\"创建对象完毕\")\n",
    "        # 别忘记写返回值哦\n",
    "        return object.__new__(cls)\n",
    "\n",
    "\n",
    "def main():\n",
    "    happy = Dog(\"Happy\")\n",
    "    print(happy)\n",
    "\n",
    "\n",
    "if __name__ == '__main__':\n",
    "    main()\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "---\n",
    "\n",
    "关于\\__name\\__在模块调用的时候会详细说，你可以先这样理解：如果直接运行py文件就执行，别人调用那么你的main就不执行了\n",
    "\n",
    "标准写法:\n",
    "```py\n",
    "# 1.导入的模块\n",
    "# 2.class的定义\n",
    "# 3.其他方法定义\n",
    "\n",
    "def main():\n",
    "    pass\n",
    "\n",
    "if __name__ == '__main__':\n",
    "    main()\n",
    "```\n",
    "其他内容后面会继续说，封装部分再说说**静态方法**和**类方法**之类的就结束了（和C#还是有很大区别的）\n",
    "\n",
    "---\n",
    "\n",
    "\n",
    "### 1.7 类属性、实例属性\n",
    "\n",
    "小明问老师：“老师老师，怎么没有静态类，静态属性之类的东西呢？”\n",
    "\n",
    "老师笑而不语道：“小家伙原来不仅仅是体重增加啊，这求知欲也是大大的增加呢 ～ 且听我慢慢道来”\n",
    "\n",
    "---\n",
    "\n",
    "类在程序里面也是对象（你姑且可以认为所有的类都类似于C#里面的静态类），而通过类实例化的对象，叫实例化对象\n",
    "\n",
    "**实例属性** --> **实例对象相互之间不共享** 一般我们都是在`__init__`中定义\n",
    "\n",
    "**类属性**（类似于C#里面的静态字段） --> 属于类对象，**多个实例对象之间共享**\n",
    "\n",
    "注意一下：**相同名称的实例属性将屏蔽掉类属性**（尽量别同名）\n",
    "\n",
    "类属性除了可以通过 **类名.类属性** 访问外，还可以直接 **实例对象.类属性** （C#中抽象类和静态类是不能被实例化的）\n",
    "\n",
    "来个案例更直观点："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1\n",
      "1\n"
     ]
    }
   ],
   "source": [
    "class Person(object):\n",
    "    # age为类属性\n",
    "    age = 1\n",
    "\n",
    "    def __init__(self, name):\n",
    "        # name为实例属性\n",
    "        self.name = name\n",
    "\n",
    "\n",
    "def main():\n",
    "    # 类名.类属性\n",
    "    print(Person.age)\n",
    "    xiaoming = Person(\"小明\")\n",
    "    # 对象.类属性\n",
    "    print(xiaoming.age)\n",
    "\n",
    "\n",
    "if __name__ == '__main__':\n",
    "    main()\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "---\n",
    "\n",
    "如果需要在类外 **修改类属性**，必须通过**类对象**去引用然后**进行修改**\n",
    "\n",
    "**如果通过实例对象去引用**，**会产生一个同名的实例属性**,这种方式修改的是实例属性，不会影响到类属性\n",
    "\n",
    "如果通过实例对象去引用该名称的属性，实例属性会强制 **屏蔽掉类属性**，即引用的是实例属性，**除非del了该实例属性**才能正常访问类属性\n",
    "\n",
    "你可以理解为，Python这么做只是为了方便你获取，该怎么修改还得怎么做。来看个案例："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1\n",
      "100\n",
      "1\n",
      "100\n",
      "22\n",
      "22\n",
      "22\n"
     ]
    }
   ],
   "source": [
    "class Person(object):\n",
    "    # age为类属性\n",
    "    age = 1\n",
    "\n",
    "    def __init__(self, name):\n",
    "        # name为实例属性\n",
    "        self.name = name\n",
    "\n",
    "\n",
    "def main():\n",
    "    # 类名.类属性\n",
    "    print(Person.age)\n",
    "\n",
    "    # 通过对象.类属性修改\n",
    "    xiaoming = Person(\"小明\")\n",
    "    xiaoming.age = 100\n",
    "    print(xiaoming.age)  # 其实，并没有修改成功，只是产生了一个同名age\n",
    "    print(Person.age)  # 对吧，类属性并没有被修改\n",
    "\n",
    "    # 通过类名修改\n",
    "    Person.age = 22  # 如果需要在类外修改类属性，必须通过类对象去引用然后进行修改\n",
    "    print(xiaoming.age)  # 刚才已经创建一个同名age，所以现在显示的是刚才的值\n",
    "    print(Person.age)  # 通过类名.类属性 就可以看到值被修改了\n",
    "\n",
    "    # 如果你还是不信，可以创建一个新对象看看\n",
    "    xiaopan = Person(\"小潘\")\n",
    "    print(xiaopan.age)\n",
    "\n",
    "    # xiaoming实例对象想访问怎么办？\n",
    "    # 除非del了该实例属性才能正常访问类属性\n",
    "    del xiaoming.age\n",
    "    print(xiaoming.age)  # 这时候访问的就是 类属性 了\n",
    "\n",
    "\n",
    "if __name__ == '__main__':\n",
    "    main()\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 1.8 实例方法、类方法、静态方法\n",
    "\n",
    "先说说 **实例方法**，实例方法第一个定义的参数只能是实例本身引用`self`,只能通过实例调用（就是我们之前用的 `def func_name(self,xxx):` ）\n",
    "\n",
    "**类方法**：是类对象所拥有的方法，需要用修饰器`@classmethod`来标识,第一个参数必须是类对象`cls`,可以通过类或者实例直用\n",
    "\n",
    "**静态方法**：定义静态方法使用装饰器`@staticmethod`,没有默认的必须参数，通过类和实例直接调用\n",
    "\n",
    "静态方法中不需要额外定义参数，因此在静态方法中引用类属性的话，必须通过 **类对象**来引用(访问)\n",
    "\n",
    "小明眼花缭乱的对老师说道，老师给我看几个案例吧："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "我叫小汪\n",
      "cls id:94310818174200\n",
      "汪汪汪\n",
      "self id:140392216464016\n",
      "我叫小汪\n",
      "cls id:94310818174200\n",
      "汪汪汪\n"
     ]
    }
   ],
   "source": [
    "class Dog(object):\n",
    "    # 类属性\n",
    "    name = \"小汪\"\n",
    "\n",
    "    # 实例方法\n",
    "    def __init__(self, age):\n",
    "        # 实例属性\n",
    "        self.age = age\n",
    "        # 打印看看\n",
    "        print(\"self id:%s\" % id(self))\n",
    "\n",
    "    # 类方法\n",
    "    @classmethod\n",
    "    def show_name(cls):\n",
    "        # 访问类属性 cls.xxx\n",
    "        print(\"我叫%s\" % cls.name)\n",
    "        # 打印看看\n",
    "        print(\"cls id:%s\" % id(cls))\n",
    "\n",
    "    # 静态方法\n",
    "    @staticmethod\n",
    "    def say_hello():\n",
    "        print(\"汪汪汪\")\n",
    "\n",
    "\n",
    "def main():\n",
    "    # 类名方式访问\n",
    "    Dog.show_name()\n",
    "    Dog.say_hello()  # 类名的方式可以访问静态方法\n",
    "\n",
    "    # 实例对象方式访问\n",
    "    dog = Dog(2)\n",
    "    dog.show_name()\n",
    "    dog.say_hello()\n",
    "\n",
    "\n",
    "if __name__ == '__main__':\n",
    "    main()\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "---\n",
    "\n",
    "一般都是这样用的（供参考）：\n",
    "\n",
    "**实例方法**：一般平时用的都是它\n",
    "\n",
    "**类方法**：类方法用在模拟C#多个构造函数(<a href=\"https://www.cnblogs.com/dotnetcrazy/p/9175950.html#ext\" target=\"_blank\">Python里面不能有同名函数</a>) or 你需要 **对类属性、类方法操作**之类的\n",
    "\n",
    "**静态方法**：一般 **都是独立功能**，类似于函数，只不过在面向对象里面一般这么用"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "---\n",
    "\n",
    "### 1.9 C#封装案例\n",
    "C#面向对象比较优美，来个封装的案例基本上就搞定了：\n",
    "```csharp\n",
    "using System;\n",
    "\n",
    "namespace _1Encapsulation\n",
    "{\n",
    "    public class Student\n",
    "    {\n",
    "        /// <summary>\n",
    "        /// 字段\n",
    "        /// </summary>\n",
    "        private int _age;\n",
    "        /// <summary>\n",
    "        /// 属性\n",
    "        /// </summary>\n",
    "        public int Age\n",
    "        {\n",
    "            get\n",
    "            {\n",
    "                return _age;\n",
    "            }\n",
    "\n",
    "            set\n",
    "            {\n",
    "                if (value > 1)\n",
    "                {\n",
    "                    _age = value;\n",
    "                }\n",
    "            }\n",
    "        }\n",
    "\n",
    "        /// <summary>\n",
    "        /// 自动化属性\n",
    "        /// </summary>\n",
    "        public string Name { get; set; }\n",
    "\n",
    "        /// <summary>\n",
    "        /// 自动属性必须要有get访问器\n",
    "        /// </summary>\n",
    "        public string SNum { get; }\n",
    "\n",
    "        private int _gender;\n",
    "        public int Gender\n",
    "        {\n",
    "            set\n",
    "            {\n",
    "                _gender = value;\n",
    "            }\n",
    "        }\n",
    "\n",
    "        /// <summary>\n",
    "        /// 构造函数的名字必须与类名一致\n",
    "        /// 构造函数没有返回值也没有viod\n",
    "        /// 默认自动生成一个无参构造函数，当有一个有参构造函数的时候无参构造函数便不会自动创建\n",
    "        /// </summary>\n",
    "        public Student() { }\n",
    "\n",
    "        /// <summary>\n",
    "        /// 有参构造函数\n",
    "        /// </summary>\n",
    "        /// <param name=\"name\"></param>\n",
    "        /// <param name=\"age\"></param>\n",
    "        public Student(string name, int age)\n",
    "        {\n",
    "            this.Name = name;\n",
    "            this.Age = age;\n",
    "        }\n",
    "\n",
    "        /// <summary>\n",
    "        /// this调用当前类的某个有参构造函数\n",
    "        /// </summary>\n",
    "        /// <param name=\"name\"></param>\n",
    "        /// <param name=\"age\"></param>\n",
    "        /// <param name=\"gender\"></param>\n",
    "        public Student(string name, int age, int gender) : this(name, age)\n",
    "        {\n",
    "            this.Gender = gender;\n",
    "        }\n",
    "\n",
    "        /// <summary>\n",
    "        /// 某个方法\n",
    "        /// </summary>\n",
    "        public void Show()\n",
    "        {\n",
    "            Console.WriteLine(\"Name:\" + this.Name + \" Age:\" + this.Age + \"\\n\");\n",
    "        }\n",
    "\n",
    "        public override string ToString()\n",
    "        {\n",
    "            return \"Name:\" + this.Name + \" Age:\" + this.Age;\n",
    "        }\n",
    "    }\n",
    "}\n",
    "```\n",
    "调用部分：\n",
    "```csharp\n",
    "using System;\n",
    "\n",
    "namespace _1Encapsulation\n",
    "{\n",
    "    class Program\n",
    "    {\n",
    "        static void Main(string[] args)\n",
    "        {\n",
    "            Student s = new Student() { Name = \"mmd\", Age = 13, Gender = 1 };\n",
    "            s.Show();\n",
    "            \n",
    "            Student s1 = new Student(\"dmm\", 20);\n",
    "            s1.Show();\n",
    "\n",
    "            Console.WriteLine(s);\n",
    "        }\n",
    "    }\n",
    "}\n",
    "```\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.5"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
